package xj.graph2d.shapes;

import static javax.swing.SwingConstants.CENTER;
import static javax.swing.SwingConstants.HORIZONTAL;
import static xj.util.xml.XMLUtil.getXMLAttributeString;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.swing.Icon;

import xj.graph2d.BoxGroup;
import xj.graph2d.DrawAttr;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.GraphNodeBase;
import xj.graph2d.Label;
import xj.graph2d.LabelListener;
import xj.graph2d.Offset;
import xj.graph2d.TextShape;
import xj.lang.CodeWriter;

public class BoxLabel 
  extends BoxGroup 
  implements Label {

  public static final int DEFAULT_ORIENTATION = HORIZONTAL;
  public static final int DEFAULT_ALIGNMENT = CENTER;

  public static final int DSIPLAY_TEXT_FULL       = 0;  
  public static final int DSIPLAY_TEXT_FULL_NB    = 1;  
  public static final int DSIPLAY_TEXT_FIRST_LINE = 2;  

  public BoxLabel() {
    this(DEFAULT_ORIENTATION, DEFAULT_ALIGNMENT);
  }

  public BoxLabel(int orientation) {
    this(orientation, DEFAULT_ALIGNMENT);
  }

  public BoxLabel(int orientation, int alignment) {
    super(orientation, alignment);
    name = "Label";
  }

  public BoxLabel(TextShape text) {
    this(null, text, DEFAULT_ORIENTATION, DEFAULT_ALIGNMENT);
  }

  public BoxLabel(IconShape iconShape, TextShape text) {
    this(iconShape, text, DEFAULT_ORIENTATION, DEFAULT_ALIGNMENT);
  }

  public BoxLabel(IconShape iconShape, TextShape text, int orientation) {
    this(iconShape, text, orientation, DEFAULT_ALIGNMENT);
  }

  public BoxLabel(IconShape iconShape, TextShape text, 
		  int orientation, int alignment) {
    super(orientation, alignment);
    name = "Label";
    if (iconShape != null) {
      iconShape.setSearchable(false);
      iconShape.showBorder(false);
      iconShape.setAllowRotation(false);
      addShape(iconShape);
    }
    if (text != null) {
      addShape(text);
    }
    arrangeShapes();
    doLayout();
  }

  @Override
  public DrawShape makeInstance() {
    return new BoxLabel(orientation, alignment);
  }

  @Override
  public void copyTo(DrawShapeBase s) 
    throws CloneNotSupportedException {
    super.copyTo(s);
    if (s instanceof BoxLabel) {
      BoxLabel l = (BoxLabel) s;
      l.setOffset(offset);
      l.islabel_ = islabel_;
      l.displayTextMode = displayTextMode;
    }
  }

  @Override
  public String toString() {
    return "Label";
  }

  public Offset getOffset() {
    return offset;
  }

  public void setOffset(Offset offset) {
    this.offset = offset;
  }

  public void setOffset(float dx, float dy) {
    if (offset != null) {
      offset.set(dx, dy);
    } else {
      offset = new Offset(dx, dy);
    }
  }

  public void adjustOffset(float dx, float dy) {
    if (offset != null) {
      offset.dx += dx;
      offset.dy += dy;
    } else {
      offset = new Offset(dx, dy);
    }
  }

  public String getDisplayText() { 
    if (displayTextMode == DSIPLAY_TEXT_FULL) { 
      return getText();
    } else if (displayTextMode == DSIPLAY_TEXT_FULL_NB) { 
      return getText(false);
    } else if (displayTextMode == DSIPLAY_TEXT_FIRST_LINE) { 
      Iterator iter = subshapes.iterator();
      if (iter.hasNext()) {
	Object o = iter.next();
	if (o instanceof TextShape) {
	  TextShape t = (TextShape) o;
	  return t.getText();
	}
      } 
    }
    return "";
  }

  public String getText() {
    return getText(true);
  }

  public String getText(boolean lb) {
    StringBuilder sb = new StringBuilder();
    boolean first = true;
    Iterator iter = subshapes.iterator();
    while (iter.hasNext()) {
      Object o = iter.next();
      if (o instanceof TextShape) {
	TextShape t = (TextShape) o;
	if (first) {
	  first = false;
	} else {
	  if (lb) {
	    sb.append('\n');
	  }
	}
	sb.append(t.getText());
      }
    }
    return sb.toString();
  }

  public void setText(String s) {
    if (s != null) {
      int n = subshapes.size();
      TextShape proto = null;
      int i = 0;
      int k = -1;
      String line = s;
      boolean moreLines = true;
      k = s.indexOf('\n');
      if (k >= 0) {
	line = s.substring(0, k);
	s = s.substring(k + 1);
      } else {
	s = null;
      }
      while (moreLines) {
	TextShape t = null;
	while (t == null && i < n) {
	  Object o = subshapes.get(i++);
	  if (o instanceof TextShape) {
	    t = (TextShape) o;
	    if (proto == null) {
	      proto = t;
	    }
	  }
	}
	if (t != null) {
	  t.setText(line);
	} else {
	  if (proto != null) {
	    try { 
	      t = (TextShape) proto.clone();
	      t.setPrompt(null);
	      t.setText(line);
	    } catch (CloneNotSupportedException e) {
	      t = new TextShape(line);
	    }
	  } else {
	    t = new TextShape(line);
	  }
	  addShape(t);
	}
	if (s != null) {
	  k = s.indexOf('\n');
	  if (k >= 0) {
	    line = s.substring(0, k);
	    s = s.substring(k + 1);
	  } else {
	    line = s;
	    s = null;
	  }
	} else {
	  moreLines = false;
	}
      }
      if (i < n) {
	for (int j = n - 1; j >= i; j--) {
	  removeShape(j);
	}
      }
      arrangeShapes();
    } else {
      removeAllShapes();
    }
    fitComponents();
  }

  public TextShape getTextShape() {
    int n = subshapes.size();
    for (int i = 0; i < n; i++) {
      Object o = subshapes.get(i);
      if (o instanceof TextShape) {
	return (TextShape) o;
      }
    }
    return null;
  }

  public void setTextAttr(String name, int style, int size, boolean underlined) {
    int n = subshapes.size();
    for (int i = 0; i < n; i++) {
      Object o = subshapes.get(i);
      if (o instanceof TextShape) {
	TextShape text = (TextShape) o;
	DrawAttr attr = text.getDrawAttr();
	if (attr == null) {
	  attr = new DrawAttr();
	  text.setDrawAttr(attr);
	}
	attr.setFont(name, style, size);
	attr.setUnderlined(underlined);
      }
    }
  }

  public void setTextFontStyle(int style) {
    int n = subshapes.size();
    for (int i = 0; i < n; i++) {
      Object o = subshapes.get(i);
      if (o instanceof TextShape) {
	TextShape text = (TextShape) o;
	DrawAttr attr = text.getDrawAttr(); 
	attr.setFontStyle(style);
      }
    }
  }

  public void setIcon(Icon icon) {
    setIcon(icon, null);
  }

  public void setIcon(Icon icon, String iconName) {
    if (icon != null) {
      int n = subshapes.size();
      IconShape ishape = null;
      for (int i = 0; i < n; i++) {
	Object o = subshapes.get(i);
	if (o instanceof IconShape) {
	  ishape = (IconShape) o;
	  break;
	}
      }
      if (ishape == null) {
	IconShape iconShape = new IconShape(icon, iconName);
	iconShape.setSearchable(false);
	iconShape.showBorder(false);
	iconShape.setAllowRotation(false);
	insertShapeAt(iconShape, 0);
      } else {
	ishape.setIcon(icon, iconName);
      }
    }
  }

  public Icon getIcon() {
    int n = subshapes.size();
    IconShape ishape = null;
    for (int i = 0; i < n; i++) {
      Object o = subshapes.get(i);
      if (o instanceof IconShape) {
	ishape = (IconShape) o;
	break;
      }
    }
    if (ishape != null) {
      return ishape.getIcon();
    } else {
      return null;
    }
  }

  public int getWidth() {
    if (bounds != null) {
      return (int) bounds.getWidth();
    }
    return 0;
  }

  public int getHeight() {
    if (bounds != null) {
      return (int) bounds.getHeight();
    }
    return 0;
  }

  public boolean isLabel() {
    return islabel_;
  }

  public void isLabel(boolean b) {
    islabel_ = b;
  }

  public int getDisplayTextMode() { 
    return displayTextMode;
  }

  public void setDisplayTextMode(int displayTextMode) { 
    this.displayTextMode = displayTextMode;
  }

  public void addLabelListener(LabelListener listener) {
    if (listener != null) {
      labelListeners.add(listener);
    }
  }

  public void removeLabelListener(LabelListener listener) {
    if (listener != null) {
      labelListeners.remove(listener);
    }
  }

  public void fireLabelChanged() {
    if (debug) {
      System.out.println("BoxLabel.fireLabelChanged()");
    }

    Iterator<LabelListener> iter = labelListeners.iterator();
    while (iter.hasNext()) {
      Object o = iter.next();
      if (o instanceof LabelListener) {
	((LabelListener) o).labelChanged(this);
      }
    }

    DrawShape parent = getParent();
    if (parent instanceof Label) {
      ((Label) parent).fireLabelChanged();
    } else if (parent instanceof GraphNodeBase) {
      LabelListener pll = ((GraphNodeBase) parent).getLabelListener();
      if (pll != null) {
	pll.labelChanged(this);
      }
    }
  }

  @Override
  protected Map<String, Object> getXMLAttributes() {
    Map<String, Object> attrs = super.getXMLAttributes();
    if (attrs == null) {
      attrs = new HashMap<String, Object>();
    }
    attrs.put("label", islabel_);
    attrs.put("display", displayTextMode);
    return attrs;
  }

  @Override
  protected void writeXMLElements(CodeWriter out, int mode,
				  String namespacePrefix) {
    if (out != null) {
      super.writeXMLElements(out, mode, namespacePrefix);

      if (offset != null) {
	String nodename = getPrefixedName("Offset", namespacePrefix);
	out.println("<" + nodename + ' ' + 
		    getXMLAttributeString("dx", offset.dx) + ' ' + 
		    getXMLAttributeString("dy", offset.dy) + "/>");
      }
    }
  }

  @Override
  protected void processXMLAttributes(org.w3c.dom.Element node) {
    if (node != null) {
      super.processXMLAttributes(node);

      String bs = node.getAttribute("label");
      islabel_ = Boolean.valueOf(bs);
      String s;
      s = node.getAttribute("display");
      try {
	displayTextMode = Integer.parseInt(s);
      } catch (NumberFormatException ex) {
      }
    }
  }

  @Override
  protected void processXMLElement(org.w3c.dom.Element e, String namespaceURI) {
    if (e != null) {
      if ("Offset".equals(e.getLocalName())) {
	String dxs, dys;
	dxs = e.getAttribute("dx");
	dys = e.getAttribute("dy");
	float dx = 0, dy = 0;
	try {
	  dx = Float.parseFloat(dxs);
	  dy = Float.parseFloat(dys);
	  if (offset == null) {
	    offset = new Offset(dx, dy);
	  } else {
	    offset.set(dx, dy);
	  }
	} catch (NumberFormatException ex) {
	}
      } else {
	super.processXMLElement(e, namespaceURI);
      }
    }
  }

  protected Offset offset;

  protected boolean islabel_ = true;

  protected int displayTextMode = DSIPLAY_TEXT_FULL;

  protected List<LabelListener> labelListeners = new ArrayList<LabelListener>();

}
